home *** CD-ROM | disk | FTP | other *** search
- /****************************************************/
- /* */
- /* SSX.C - stack swap executive */
- /* */
- /* By Tom Green and Dennis Cronin */
- /* 10/19/92 */
- /* */
- /****************************************************/
-
- /* turn on inline asm */
- #pragma inline
-
- #include <alloc.h>
- #include <dos.h>
- #include <string.h>
- #include <setjmp.h>
- #include "ssx.h"
- #include "ssx_conf.h"
-
- /* Task Control Block */
-
- typedef struct tcb {
- /* task chain forward ptr */
- struct tcb *forw;
- /* task chain backward ptr */
- struct tcb *back;
- /* delay chain forward ptr */
- struct tcb *dforw;
- /* delay chain backward ptr */
- struct tcb *dback;
- /* pointer to task code */
- fptr task_ptr;
- /* pointer to start of allocated stack */
- unsigned int *stack;
- /* task's current stack pointer */
- unsigned int *stack_ptr;
- /* delay counter */
- long timeout;
- /* flag for task timed out */
- unsigned char timedout;
- /* flag for TCB in use */
- unsigned char active;
- /* status flags */
- unsigned char status;
- /* task priority */
- unsigned char priority;
- /* task ID */
- unsigned char id;
- /* for storing extra task context */
- char context[CNTXT_SZ];
- } tcb;
-
-
- /* misc. defines */
- #define TRUE 1
- #define FALSE 0
-
- /* background task defines */
- #define BG_TASK_ID 0xff
- #define BG_TASK_PRI 0xff
-
- /* make data and code local to this file */
- #define LOCAL static
-
- /* flags for the TCB status word */
- #define T_READY 0 /* ready to run */
- #define T_WAITING 1 /* waiting on wait_q */
- #define T_DELAYED 2 /* delay timer running */
-
-
- /* local function prototypes */
-
- LOCAL tcb *get_tcb(void);
- LOCAL void free_tcb(tcb *tbp);
- LOCAL void put_ready(tcb *tbp);
- LOCAL void rotate_tasks(tcb *tbp);
- LOCAL void put_delay(long timeout);
- LOCAL void run_new_task(void);
- LOCAL void bg_task(void);
- LOCAL void stack_swap(unsigned int **old_stack_ptr,
- unsigned int **new_stack_ptr);
- LOCAL int disable_ints(void);
-
-
- /* local variables */
-
- LOCAL long sys_time; /* system timer */
- LOCAL unsigned char slice_cnt;
- LOCAL int running;
- LOCAL int initd;
- LOCAL jmp_buf jbuf;
- LOCAL int switch_lock;
-
- /* task control */
- LOCAL tcb t_pool[MAX_TASKS + 1]; /* pool of TCBs */
- LOCAL tcb t_ready; /* head of ready task queue */
- LOCAL tcb t_null; /* the NULL task */
- LOCAL tcb *t_free; /* head of free queue */
- LOCAL tcb *t_current; /* pointer to current task */
-
- /* delay control */
- LOCAL tcb d_chain; /* q head for delayed tasks */
-
- /* MACROS to unlink from task and delay queues */
-
- /* t_unlink - must be used w/ interrupts off */
- #define t_unlink(tbp) \
- { \
- (tbp)->back->forw = (tbp)->forw; \
- if((tbp)->forw != NULL) \
- (tbp)->forw->back = (tbp)->back; \
- }
-
- /* d_unlink - must be used w/ interrupts off */
- #define d_unlink(tbp) \
- { \
- (tbp)->dback->dforw = (tbp)->dforw; \
- if((tbp)->dforw != NULL) \
- (tbp)->dforw->dback = (tbp)->dback; \
- }
-
-
- /*
- * ssx_init - init ssx data
- */
-
- int
- ssx_init(void)
- {
- int i;
- tcb *tcbp;
-
- if(initd)
- return(INIT_ERROR);
-
- memset(&d_chain,0,sizeof(d_chain));
-
- /* init TCB free queue links */
- for(i=0,tcbp=t_pool;i < MAX_TASKS-1;i++,tcbp++){
- tcbp->forw = &t_pool[i+1];
- }
- t_pool[i].forw = NULL;
-
- for(i = 0;i < MAX_TASKS;i++){
- t_pool[i].active=FALSE;
- }
-
- t_current = NULL;
- t_free = t_pool;
- t_ready.forw = NULL;
- switch_lock = 0;
-
- /* set up background task */
- if((ssx_task_create(BG_TASK_PRI,BG_TASK_ID,
- bg_task,0x200,"bg_task"))
- != SUCCESS)
- return(INIT_ERROR);
-
- initd = TRUE;
-
- return(SUCCESS);
- }
-
-
- /*
- * sx_run - this starts executive
- */
-
- void
- ssx_run(void)
- {
- int val;
-
- val = setjmp(jbuf);
-
- if(val != 0)
- return;
-
- slice_cnt = 0;
- sys_time = 0;
-
- /* make current task ptr point to dummy tcb so
- * beginning of time stack pointer save will
- * have a safe place to save to.
- */
-
- t_current = &t_null;
-
- /* mark SSX as active */
- running = TRUE;
-
- /* this will start the first task rolling */
- ssx_switch();
- }
-
- /*
- * sx_stop - this stops executive
- */
-
- void
- ssx_stop(void)
- {
- int i;
- int_state_var istate;
-
- ints_off(istate);
-
- /* free any allocated stacks */
- for(i = 0; i < MAX_TASKS; i++){
- if(t_pool[i].stack != NULL){
- free(t_pool[i].stack);
- t_pool[i].stack = NULL;
- }
- }
- initd = FALSE;
- running = FALSE;
- restore_ints(istate);
-
- longjmp(jbuf,1);
- }
-
-
- /*
- * ssx_task_create - create task and set up tcb
- */
-
- int
- ssx_task_create(unsigned char task_pri,
- unsigned char task_id,fptr task_ptr,
- unsigned int stack_size,char *name)
- {
- unsigned int i;
- tcb *tbp;
- int_state_var istate;
-
- ints_off(istate);
-
- if(task_id == 0) {
- restore_ints(istate);
- return(TID_ERR);
- }
-
- /* check for TID already in use */
- for(i = 0,tbp = t_pool;i < MAX_TASKS;i++,tbp++) {
- if(tbp->active && tbp->id == task_id) {
- restore_ints(istate);
- return(TID_ERR);
- }
- }
-
- if((tbp = get_tcb()) == NULL) { /* get a tcb */
- restore_ints(istate);
- return(TCB_ERR);
- }
-
-
- /* allocate stack for this task */
- if((tbp->stack = (unsigned int *)
- malloc(stack_size)) == NULL){
- restore_ints(istate);
- return(STACK_ERR);
- }
-
- /* fill in the blanks */
- strncpy(tbp->context,name,CNTXT_SZ);
- tbp->priority = task_pri;
- tbp->id = task_id;
- tbp->status = T_READY;
- tbp->timedout = FALSE;
- tbp->timeout = 0L;
- tbp->forw = tbp->back =
- tbp->dforw = tbp->dback = NULL;
-
- tbp->task_ptr = task_ptr;
-
- tbp->stack_ptr = (unsigned int *)(tbp->stack +
- (stack_size / 2));
-
- /* setup task stack to have address of start up
- * routine and fake di, si, bp registers to pop.
- * This part is not portable. the stack looks
- * like this:
- *
- * |-------------------------| high
- * |address of run_new_task |
- * |-------------------------|
- * |bp |
- * |-------------------------|
- * |si |
- * |-------------------------|
- * |di |
- * |-------------------------| low
- *
- */
-
- *(--tbp->stack_ptr) = (unsigned int)run_new_task;
- *(--tbp->stack_ptr) = 0; /* fake BP,SI,DI */
- *(--tbp->stack_ptr) = 0; /* on stack */
- *(--tbp->stack_ptr) = 0;
-
- /* put on active chain */
- rotate_tasks(tbp);
-
- ssx_switch();
-
- restore_ints(istate);
-
- return(SUCCESS);
- }
-
-
- /*
- * ssx_task_delay - cause task to delay for number
- * of ticks
- */
-
- void
- ssx_task_delay(long timeout)
- {
- int_state_var istate;
-
- ints_off(istate);
-
- if(timeout == 0) {
- ssx_switch();
- restore_ints(istate);
- return;
- }
-
- put_delay(timeout); /* put current task on */
- /* delay queue */
- t_unlink(t_current); /* take off ready queue */
-
- ssx_switch();
- restore_ints(istate);
- }
-
-
- /*
- * ssx_task_delete - delete a task and remove from
- * queues
- */
-
- int
- ssx_task_delete(unsigned char task_id)
- {
- unsigned int i;
- tcb *tp;
- int_state_var istate;
-
- ints_off(istate);
-
- /* look for background task ID */
- if(task_id == BG_TASK_ID){
- restore_ints(istate);
- return(TID_ERR);
- }
-
- /* look for 'self' form */
- if(task_id == 0) {
- if(t_current) {
- /* get current task's id */
- task_id = t_current->id;
- }
- }
-
- /* brute force, search all TCBs for matching ID */
- for(i = 0,tp = t_pool;i < MAX_TASKS;i++,tp++) {
- if(tp->active && tp->id == task_id) {
- break;
- }
- }
-
-
- /* see if found match */
- if(i == MAX_TASKS){
- restore_ints(istate);
- return(TID_ERR);
- }
-
- switch(tp->status & (T_DELAYED | T_WAITING)) {
- case T_DELAYED:
- d_unlink(tp); /* remove from delay q */
- break;
- case T_WAITING:
- t_unlink(tp); /* remove from some */
- /* wait_q */
- break;
- case T_DELAYED | T_WAITING:
- t_unlink(tp); /* remove from some */
- /* wait_q */
- d_unlink(tp); /* remove from delay q */
- break;
- case T_READY:
- t_unlink(tp); /* remove from ready q */
- break;
- }
-
- free(tp->stack); /* free allocated stack */
- tp->stack = NULL;
- free_tcb(tp); /* free up the TCB */
- ssx_switch();
- restore_ints(istate);
- return(SUCCESS);
- }
-
-
-
- /*
- * ssx_change_priority - change priority for currently
- * running task,
- * don't immediately reschedule
- * returns old priority
- */
-
- unsigned char
- ssx_change_priority(unsigned char new_priority)
- {
- unsigned char old_priority;
- int_state_var istate;
-
- ints_off(istate);
- old_priority = t_current->priority;
- t_current->priority = new_priority;
- t_unlink(t_current);
- rotate_tasks(t_current);
- restore_ints(istate);
- return(old_priority);
- }
-
-
- /*
- * ssx_wait - wait on wait_q. reschedule later
- */
-
- void
- ssx_wait(wait_q *wqptr)
- {
- tcb *tp;
- tcb *t_cur;
- int_state_var istate;
-
- ints_off(istate);
-
- /* check for message flag already set */
- if(wqptr->mesg_flg){
- wqptr->mesg_flg = FALSE;
- restore_ints(istate);
- return;
- }
-
- t_cur = t_current;
- t_unlink(t_cur); /* take off ready queue */
-
- tp = (tcb *)&wqptr->task_ptr;
-
- /*
- * find where to insert waiting task into
- * wait queue
- */
- while((tp->forw) != NULL) {
- if(t_cur->priority <= tp->forw->priority)
- break;
- tp = tp->forw;
- }
-
- /* insert into queue */
- if((t_cur->forw = tp->forw) != NULL)
- t_cur->forw->back = t_cur;
- tp->forw = t_cur;
- t_cur->back = tp;
- t_cur->status = T_WAITING;
-
- ssx_switch();
- restore_ints(istate);
- }
-
-
- /*
- * ssx_wait_with alarm - wait on wait_q with alarm.
- * reschedule now
- */
-
- int
- ssx_wait_with_alarm(wait_q *wqptr,long timeout)
- {
- tcb *tp;
- tcb *t_cur;
- int_state_var istate;
-
- ints_off(istate);
-
- /* check for message flag already set */
- if(wqptr->mesg_flg){
- wqptr->mesg_flg = FALSE;
- restore_ints(istate);
- return(SUCCESS);
- }
-
- t_cur = t_current;
- t_unlink(t_cur); /* take off ready queue */
-
- tp = (tcb *)&wqptr->task_ptr;
-
- /*
- * find where to insert waiting task into
- * wait queue
- */
- while((tp->forw) != NULL) {
- if(t_cur->priority <= tp->forw->priority)
- break;
- tp = tp->forw;
- }
-
- /* insert into queue */
- if((t_cur->forw = tp->forw) != NULL)
- t_cur->forw->back = t_cur;
- tp->forw = t_cur;
- t_cur->back = tp;
- t_cur->status = T_WAITING;
-
- /*
- * if there is timeout value, put task on
- * delay queue
- */
- if(timeout)
- put_delay(timeout);
-
- ssx_switch();
-
- /*
- * we were sheduled back in so
- * see if task timed out and return error
- */
-
- if(t_cur->timedout){
- t_cur->timedout = FALSE;
- restore_ints(istate);
- return(TO_ERR);
- }
-
- restore_ints(istate);
-
- /* task did not time out, so return success */
- return(SUCCESS);
- }
-
-
- /*
- * ssx_alert - alert wait_q. reshedule if task
- * alerted is equal or higher
- * priority than current task
- */
-
- int
- ssx_alert(wait_q *wqptr)
- {
- tcb *np;
- tcb *oldtcb;
- int_state_var istate;
-
- ints_off(istate);
-
- /* check for message waiting */
- if(wqptr->mesg_flg){
- restore_ints(istate); /* cannot alert if */
- return(MW_ERR); /* messgae is waiting */
- }
-
- np=(tcb *)wqptr->task_ptr;
-
- /* check if there is a task waiting on wait_q */
- if(np != NULL){
- t_unlink(np);
- if(np->status & T_DELAYED)
- d_unlink(np);
- np->status &= ~(T_WAITING | T_DELAYED);
- put_ready(np);
- /*
- * switch to waiting task if it is equal
- * or higher priority
- */
- if(np->priority <= t_current->priority){
- oldtcb = t_current;
- t_current = np;
- /*
- * check and see if scheduling is
- * disabled
- */
- if(switch_lock == 0)
- stack_swap(&oldtcb->stack_ptr
- ,&np->stack_ptr);
- }
- restore_ints(istate);
- return(SUCCESS);
- }
-
- /* fell thru, simply leave message in wait_q */
- wqptr->mesg_flg = TRUE;
- restore_ints(istate);
- return(SUCCESS);
- }
-
-
- /*
- * ssx_clock_tick - call this to update ssx clock from
- * timer interrupt handler
- */
-
- void
- ssx_clock_tick(void)
- {
- tcb *tp;
- int_state_var istate;
-
- ints_off(istate);
-
- if(running == FALSE){
- restore_ints(istate);
- return;
- }
-
- sys_time++;
-
- /* do time updates */
- tp = (tcb *)&d_chain;
- /* check for timed out tasks */
- while((tp = tp->dforw) != NULL) {
- if((sys_time - tp->timeout) >= 0) {
- d_unlink(tp); /* this one's ready */
- tp->timedout = TRUE;
- if(tp->status & T_WAITING)
- t_unlink(tp);
- tp->status = T_READY;
- /* put task on ready queue */
- rotate_tasks(tp);
- }
- else
- break; /* passed the ready ones */
- }
-
- /* round robin rotation */
- if((++slice_cnt) == TIME_SLICE){
- slice_cnt = 0;
- /* if task is running and was left ready */
- if(t_current && t_current->status
- == T_READY) {
- /* remove from ready queue */
- t_unlink(t_current);
- /* puts at back of pri group */
- rotate_tasks(t_current);
- }
- }
-
- ssx_switch();
- restore_ints(istate);
- }
-
-
- /*
- * ssx_set_time - this sets SSX system time
- */
-
- void
- ssx_set_time(long time)
- {
- int_state_var istate;
-
- ints_off(istate);
- sys_time = time;
- restore_ints(istate);
- }
-
-
- /*
- * ssx_get_time - this returns SSX system time
- */
-
- long
- ssx_get_time(void)
- {
- return(sys_time);
- }
-
-
- /*
- * ssx_lock - disable task switching
- */
-
- void
- ssx_lock(void)
- {
- int_state_var istate;
-
- ints_off(istate);
- switch_lock++;
- restore_ints(istate);
- }
-
- /*
- * ssx_unlock - enable task switching
- */
-
- void
- ssx_unlock(void)
- {
- int_state_var istate;
-
- ints_off(istate);
- /* call ssx_switch if we are not nested */
- if(--switch_lock == 0)
- ssx_switch();
- restore_ints(istate);
- }
-
- /*
- * ssx_switch - run next ready task
- *
- * notes: there must always be a runnable task w/
- * SSX. a background task is created by ssx_init
- * and this task can never wait or do anything
- * that would remove it from the active queue.
- * this saves checks in this routine, making
- * it more efficient.
- */
-
- void
- ssx_switch(void)
- {
- tcb *oldtcb;
-
- if(!running)
- return;
-
- oldtcb = t_current;
-
- /* switch tasks */
- t_current = t_ready.forw; /* get next */
- /* ready task */
- /*
- * if new task is same as old, do not
- * bother with switch
- */
- if(t_current == oldtcb)
- return;
-
- /* check and see if scheduling is disabled */
- if(switch_lock == 0){
- /* we have a new task so do a task switch */
- stack_swap(&oldtcb->stack_ptr,
- &t_current->stack_ptr);
- }
- }
-
-
-
- /*
- * get_tcb - get task control block
- */
-
- LOCAL tcb *
- get_tcb(void)
- {
- tcb *tbp;
-
- if(t_free == NULL)
- return(NULL);
- tbp = t_free;
- t_free = tbp->forw;
- tbp->active = TRUE;
-
- return(tbp);
- }
-
-
- /*
- * free_tcb - free task control block
- */
-
- LOCAL void
- free_tcb(tcb *tbp)
- {
- /* '****' for debug TCB not in use */
- tbp->timeout = 0x2a2a2a2aL;
- tbp->active = FALSE;
- tbp->forw = t_free;
- t_free = tbp;
- }
-
-
- /*
- * put_ready - put task at head of ready queue
- */
-
- LOCAL void
- put_ready(tcb *tbp)
- {
- tcb *tp,*np;
- unsigned int priority;
-
- /* get priority of task to be inserted in chain */
- priority = tbp->priority;
-
- /* put on the active chain */
- tp = (tcb *)&t_ready;
- /*
- * sort in order of decreasing priority, put at
- * head of pri group
- */
- while((np = tp->forw) != NULL && np->priority
- < priority)
- tp = np;
- /* link in */
- tbp->forw = np;
- tbp->back = tp;
- tp->forw = tbp;
- if(np != NULL)
- np->back = tbp;
- }
-
- /*
- * rotate_tasks - put task at back of ready queue
- */
-
- LOCAL void
- rotate_tasks(tcb *tbp)
- {
- tcb *tp,*np;
- unsigned int priority;
-
- /* get priority of task to be inserted in chain */
- priority = tbp->priority;
-
- /* put on the active chain */
- tp = (tcb *)&t_ready;
- /*
- * sort in order of decreasing priority,
- * put at back of pri group
- */
- while((np = tp->forw) != NULL && np->priority
- <= priority)
- tp = np;
- /* link in */
- tbp->forw = np;
- tbp->back = tp;
- tp->forw = tbp;
- if(np != NULL)
- np->back = tbp;
- }
-
- /*
- * put_delay - put task on delay queue
- */
-
- LOCAL void
- put_delay(long timeout)
- {
- tcb *tp,*np;
-
- t_current->timeout =
- timeout + sys_time; /* actual time ready */
- t_current->timedout=FALSE;
- t_current->status |= T_DELAYED;
- tp = (tcb *)&d_chain;
- /* sort in order increasing target time */
- /* trick to solve wrap of sys_time */
- while((np = tp->dforw) != NULL) {
- if(timeout <= np->timeout - sys_time)
- /* hit a more future one */
- break;
- tp = np;
- }
- /* link in */
- t_current->dforw = np;
- t_current->dback = tp;
- tp->dforw = t_current;
- if(np != NULL)
- np->dback = t_current;
- }
-
-
- /*
- * run_new_task - starts up a new task making sure
- * interrupts are enabled
- */
-
- LOCAL void
- run_new_task(void)
- {
- ints_on();
- (t_current->task_ptr)();
- }
-
- /*
- * bg_task - must have a background task
- */
-
- LOCAL void
- bg_task(void)
- {
- while(1);
- }
-
-
- /*
- * WARNING - routines from here on are not portable
- */
-
- /*
- * stack_swap - switch from stack of curent task to
- * stack of new task
- */
-
- LOCAL void
- stack_swap(unsigned int **old_stack_ptr,
- unsigned int **new_stack_ptr)
- {
- asm or di,di /* fool compiler into saving */
- asm or si,si /* di and si registers */
-
- /* save stack pointer of old task from sp reg */
- *old_stack_ptr = (unsigned int *)_SP;
-
- /* load sp reg with stack pointer of new task */
- _SP=(unsigned int)*new_stack_ptr;
- }
-
-
- /*
- * disable_ints - disable interrupts and return state
- * of interrupts before before they
- * were disabled. Returns positive
- * integer if they were enabled
- */
-
- LOCAL int
- disable_ints(void)
- {
- asm pushf /* save flags to get */
- /* interupt status */
- asm cli /* interrupts off */
- asm pop ax /* get interrupt state from */
- /* flags that were pushed */
- asm and ax,0200h /* and flags to get interrupt */
- /* status */
- return(_AX);
- }